﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Web;
using SecurityClient;

namespace SecurityModule.Workflows
{
    class WFPostHomeCreateUser : SimpleWorkflow
    {
        public WFPostHomeCreateUser()
        {
            Verb = "POST";
            Path = "/Home/CreateUser";
        }
        public override void PreProcessImplementation(HttpContext context)
        {
            // verify token, store details of the account pending approval and add Email and ClientCode to Request.Form
            var callback = new Func<Stream, Stream, bool>((inputStream, outputStream) =>
                {
                    //Parse multi-part request stream, and then rewrite it
                    var multipart = new MultipartFormParser.MultipartFormParser();
                    var fields = multipart.Parse(inputStream, context.Request.ContentEncoding);

                    //Read fields
                    var tokenId = fields["VerificationToken"].Value;
                    var password = fields["Password"].Value;
                    var password2 = fields["Password2"].Value;
                    var yubikeyOTP = fields["YubikeyOTP"].Value;

                    //Whitelist the following fields to prevent password from being sidebanded.
                    string[] whitelistFields =
                        {
                            "FirstName", "LastName",
                            "DataExchangeAgreement", "VerificationToken", "Email"
                        };
                    foreach (var key in fields.Keys.ToArray())
                    {
                        if (!whitelistFields.Contains(key))
                            fields.Remove(key);
                    }

                    var hasErrors = false;
                    if (string.IsNullOrEmpty(tokenId))
                    {
                        fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERRMSG",
                                Value = "Missing verification token"
                            });
                        hasErrors = true;
                    }
                    if (string.IsNullOrEmpty(password))
                    {
                        fields.Add("EXTERR_Password", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERR_Password",
                                Value = "Please provide a password"
                            });
                        hasErrors = true;
                    }
                    if (string.IsNullOrEmpty(password2))
                    {
                        fields.Add("EXTERR_Password2", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERR_Password2",
                                Value = "Please confirm your password"
                            });
                        hasErrors = true;
                    }
                    if (!hasErrors && !password.Equals(password2))
                    {
                        fields.Add("EXTERR_Password2", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "EXTERR_Password2",
                                Value = "Passwords don't match"
                            });
                        hasErrors = true;
                    }
                    if (hasErrors)
                    {
                        var p = multipart.GetMultipartFormData(fields);
                        outputStream.Write(p, 0, p.Length);

                        return true;
                    }
                    try
                    {
                        //Anon is attempting to confirm their identity
                        var security = new SecurityServiceProxy();
                        var email = security.GetTokenFieldValue(tokenId, "EmailAddress") as string;
                        var clientCode = security.GetTokenFieldValue(tokenId, "ClientCode") as string;
                        if (string.IsNullOrEmpty(email) || string.IsNullOrWhiteSpace(clientCode))
                        {
                            // send up an error to the application
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERRMSG",
                                    Value = "Invalid invitation token"
                                });
                            var p = multipart.GetMultipartFormData(fields);
                            outputStream.Write(p, 0, p.Length);

                            return true;
                        }

                        // token is verified

                        //Create a token to be used later to actually create the account
                        var result = security.RequestCreateUser(email, password, yubikeyOTP, tokenId);

                        if (result.Success)
                        {
                            fields.Add("Token", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "Token",
                                    Value = result.TokenId
                                });
                            fields["Email"].Value = email;

                            fields.Add("ClientCode", new MultipartFormParser.MultipartFormParser.Field
                            {
                                Name = "ClientCode",
                                Value = clientCode
                            });
                        }
                        else
                        {
                            //Bubble up an error to the application
                            fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERRMSG",
                                    Value = result.ErrorMessage
                                });
                        }
                    }
                    catch (Exception exception)
                    {
                        if (exception is PipeException || exception is EndpointNotFoundException)
                        {
                            //If the security service is offline or broken
                            if (fields.ContainsKey("EXTERRMSG"))
                            {
                                fields["EXTERRMSG"].Value = SecurityServiceDownMsg;
                            }
                            else
                            {
                                fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                    {
                                        Name = "EXTERRMSG",
                                        Value = SecurityServiceDownMsg
                                    });
                            }
                        }
                        else
                        {
                            if (fields.ContainsKey("EXTERRMSG"))
                            {
                                fields["EXTERRMSG"].Value = exception.Message;
                            }
                            else
                            {
                                fields.Add("EXTERRMSG", new MultipartFormParser.MultipartFormParser.Field
                                {
                                    Name = "EXTERRMSG",
                                    Value = exception.Message
                                });
                            }
                        }
                    }

                    //Write request stream
                    var post = multipart.GetMultipartFormData(fields);
                    outputStream.Write(post, 0, post.Length);

                    return true;
                });
            context.Request.Filter = new RequestFilter(context.Request.Filter, callback);

            // show Yubikey Field
            context.Response.Filter = new ReplaceFilter(
                context.Response.Filter,
                context.Response.ContentEncoding,
                new Dictionary<string, string>
                    {
                        {
                            "{{YubikeyOTP}}",
                            @"<label class='control-label' for='YubikeyOTP'>Yubikey</label><input name='YubikeyOTP' type='text' class='form-control'>"
                        }
                    }
                );
        }

        public override void PostProcessImplementation(HttpContext context)
        {
            // No post-processing
        }
    }
}
